package triggerhelper.controllers;

import java.awt.event.KeyEvent;
import triggerhelper.errorChecks.possibleErrors.*;
import java.util.ArrayList;
import javax.swing.*;
import triggerhelper.dataHolders.*;
import triggerhelper.errorChecks.ErrorCheck;
import triggerhelper.tabs.VariablesTab;
import triggerhelper.triggerCreator;

public class VariableController {

    //******************
    //Easy access fields
    //******************
    private JTextField fVarName, fInstance, fStart, fEnd, fIterate;
    private JComboBox cbVars, cbOptions;
    private JButton bSubOptions, bHelp, bSubInstance, bChangeInstance;
    private JList listInstance;
    private JTabbedPane tabPane;
    private JLabel mainProject, mainTrigger;
    private Trigger currentTrigger;
    private VariablesTab theTab;
    private triggerCreator theParent;

    private DefaultListModel listModel;
    private boolean noVars = true;
    private ErrorCheck eVarName, eInstance, eMin, eMax, eIterate;

    public VariableController(VariablesTab a, triggerCreator b) {
        theTab = a;
        theParent = b;
        setErrorChecks();
        setEasyAccessFields(a);
        listModel = (DefaultListModel)listInstance.getModel();
        mainProject = b.getMainProject();
        mainTrigger = b.getMainTrigger();
        setListeners();
    }

    private void setListeners(){
        theTab.addComponentListener(new java.awt.event.ComponentAdapter() {
            @Override
            public void componentShown(java.awt.event.ComponentEvent evt) {
                loadPane();
            }
        });
        cbVars.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selectVariable();
            }
        });
        listInstance.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            @Override
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                selectInstance();
            }
        });
        listInstance.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_DELETE) {
                    deleteInstance();
                }
            }
        });
        bSubInstance.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                submitInstance();
            }
        });
        bChangeInstance.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                changeInstance();
            }
        });
        bSubOptions.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                submitOption();
            }
        });
        bHelp.addActionListener(new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                help();
            }
        });
        fVarName.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                fVarName.selectAll();
            }
        });
        fInstance.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                fInstance.selectAll();
            }
        });
        fInstance.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                    submitInstance();
                    fInstance.selectAll();
                }
            }
        });
        fStart.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                fStart.selectAll();
            }
        });
        fStart.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                    fEnd.requestFocusInWindow();
                }
            }
        });
        fEnd.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                fEnd.selectAll();
            }
        });
        fEnd.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                    fIterate.requestFocusInWindow();
                }
            }
        });
        fIterate.addFocusListener(new java.awt.event.FocusAdapter() {
            @Override
            public void focusGained(java.awt.event.FocusEvent evt) {
                fIterate.selectAll();
            }
        });
        fIterate.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                    cbOptions.requestFocusInWindow();
                    cbOptions.setSelectedIndex(1);
                }
            }
        });
        cbOptions.addKeyListener(new java.awt.event.KeyAdapter() {
            @Override
            public void keyPressed(java.awt.event.KeyEvent evt) {
                if (evt.getKeyCode() == KeyEvent.VK_ENTER) {
                    submitOption();
                }
            }
        });
    }

    private void setEasyAccessFields(VariablesTab a) {
        fVarName = a.getfVarName();
        fInstance = a.getfVarInstance();
        fStart = a.getfStartValue();
        fEnd = a.getfEndValue();
        fIterate = a.getfIterate();
        cbVars = a.getCbVars();
        cbOptions = a.getCbOptions();
        bSubOptions = a.getbSubmitOptions();
        bHelp = a.getBhelp();
        bSubInstance = a.getbSubmitInstance();
        bChangeInstance = a.getbChange();
        listInstance = a.getListInstance();
        tabPane = a.getTabPane();
        currentTrigger = null;
    }

    private void setErrorChecks(){
        eVarName = new ErrorCheck("Variable Name", theTab);
        eVarName.addError(new AlphaNumeric(true));
        eVarName.addError(new MaxLength(20));

        eInstance = new ErrorCheck("Instance", theTab);
        eInstance.addError(new MaxLength(40));

        eMin = new ErrorCheck("Start Value", theTab);
        eMin.addError(new MinMax(0, 50000));
        eMax = new ErrorCheck("End Value", theTab);
        eMax.addError(new MinMax(0, 50000));

        eIterate = new ErrorCheck("Iteration", theTab);
        eIterate.addError(new MinMax(0, 5000));
    }

    public void loadPane() {
        //1) load up trigger's variables, if any
        currentTrigger = (Trigger)theParent.getCurrentTrigger();
        Trigger tempT = currentTrigger;
        if(noVars){
            variableNowExists();
        }
        cbVars.removeAllItems();
        ArrayList<Variable> tempVars = new ArrayList<Variable>(tempT.getVariables());
        tempVars.addAll(tempT.getUnsavedVariables());
        for (int i = 0; i < tempVars.size(); i++) {
            cbVars.addItem(tempVars.get(i));
        }
        
        //1) disable what needs disabling
        if (cbVars.getItemCount() == 0) {
            noVariables();
        }
    }

    public void selectInstance() {
        selectInstance(listInstance.getSelectedIndex());
    }
    private void selectInstance(int index) {
        if(index == -1){
            return;
        }
        Variable tempV = (Variable) cbVars.getSelectedItem();
        if (!tempV.isNumber()) {
            fInstance.setText(tempV.getInstance(index));
            tabPane.setSelectedIndex(0);
        } else {
            fStart.setText("" + tempV.getMinValue());
            fEnd.setText("" + tempV.getMaxValue());
            tabPane.setSelectedIndex(1);
        }
    }

    public void submitInstance() {
        //1) get field content -> error check
        Variable tempV = (Variable) cbVars.getSelectedItem();
        if(!tempV.getHasSaved() || !tempV.isNumber()){
            String tempI = fInstance.getText();
            if (eInstance.hasNoErrors(tempI)){
                //2) place into instance list, input into the variable
                listModel.addElement(tempI);
                int index = listModel.size()-1;
                listInstance.setSelectedIndex(index);
                listInstance.ensureIndexIsVisible(index);
            }
        }else{
            JOptionPane.showMessageDialog(theParent, "I'm sorry, the variable selected is a number and cannot have instances of type string.");
        }
    }

    private void changeInstance() {
        //1) get field content -> error check
        if(listInstance.getSelectedIndex() == -1){
            JOptionPane.showMessageDialog(theParent, "I'm sorry, you do not have an instance selected.");
            return;
        }
        Variable tempV = (Variable) cbVars.getSelectedItem();
        if(!tempV.getHasSaved() || !tempV.isNumber()){
            String tempI = fInstance.getText();
            if (eInstance.hasNoErrors(tempI)){
                //2) place into instance list, input into the variable
                int index = listInstance.getSelectedIndex();
                listModel.setElementAt(tempI, index);
            }
        }else{
            JOptionPane.showMessageDialog(theParent, "I'm sorry, the variable selected is a number and cannot have instances of type string.");
        }
    }

    private void deleteInstance() {
        if(listModel.isEmpty()){
            JOptionPane.showMessageDialog(theTab, "I'm sorry, there are no instances here for you to delete.");
            return;
        }
        Variable tempV = (Variable)cbVars.getSelectedItem();
        if(tempV.isNumber()){ //make sure not to delete number variables
            JOptionPane.showMessageDialog(theTab, "I'm sorry, you may change the min and max values, but not delete them.");
        }else{
            // 1) remove selected instance
            int index = listInstance.getSelectedIndex();
            listModel.remove(index);

            // 2) find next available instance
            if(index == listModel.size()) {
                index--;
            }

            // 3) select instance
            listInstance.setSelectedIndex(index);
            listInstance.ensureIndexIsVisible(index);
        }
    }

    public void submitOption() {
        //1) int a = selected Option
        int a = cbOptions.getSelectedIndex();
        //2) case statement -> newVariable(), saveVariable(), deleteVariable()
        switch (a) {
            case 0:
                newVariable();
                break;
            case 1:
                saveVariable();
                break;
            case 2:
                deleteVariable();
                break;
            case 3:
                deleteInstance();
                break;
        }
    }

    public void help() {
        //1) show related help topic
    }
    //**************
    //helper methods
    //**************
    public void selectVariable() {
        if(cbVars.getSelectedIndex() == -1){
            return;
        }
        //1) select <index> variable - load up instances
        Variable tempV = (Variable) cbVars.getSelectedItem();
        fVarName.setText(tempV.getName());
        if(tempV.getHasSaved()){
            if(tempV.isNumber()){
                tabPane.setEnabledAt(0, false);
                tabPane.setEnabledAt(1, true);
            }else{
                tabPane.setEnabledAt(0, true);
                tabPane.setEnabledAt(1, false);
            }
            //2) load up correct fields on the left
            fIterate.setText("" + tempV.getIterate());
            selectInstance(0);
        }else{
            setFieldsToDefault();
        }
        listInstance.setModel(tempV.getInstances());
        listModel = (DefaultListModel)listInstance.getModel();
    }

    private void newVariable() {
        boolean valid = false;
        String varName = "";
        while (!valid) {
            varName = JOptionPane.showInputDialog(theTab, "Please enter a variable name");
            if(varName == null){
                return;
            }
            ArrayList<Variable> varList = currentTrigger.getVariables();
            ArrayList<String> varNameList = new ArrayList<String>();
            for(final Variable v : varList){
                varNameList.add(v.getName());
            }
            valid = eVarName.hasNoErrors(varName, varNameList);
            
        }
        if(noVars){
            variableNowExists();
        }
        Variable unsaved = new Variable(currentTrigger, varName);
        cbVars.addItem(unsaved);
        currentTrigger.addUnsavedVariable(unsaved);
        fVarName.setText(varName);
        setFieldsToDefault();
        int select = cbVars.getItemCount() - 1;
        cbVars.setSelectedIndex(select);
    }

    private void saveVariable() {
        boolean variablesExist = (cbVars.getItemCount() != 0);
        if (!variablesExist) {
            JOptionPane.showMessageDialog(theTab, "I'm sorry, there are no variables for you to save.");
            return;
        }

        System.out.println("check 1");

        //check to distinguish number/word
        Variable tempV = (Variable) cbVars.getSelectedItem();
        boolean isAnumber = false;
        if ((tempV.getHasSaved() && tempV.isNumber()) || (!tempV.getHasSaved() && tabPane.getSelectedIndex() == 1)) {
            isAnumber = true;
            String sStart = fStart.getText();
            String sEnd = fEnd.getText();
            if(!eMin.hasNoErrors(sStart) || !eMax.hasNoErrors(sEnd)){
                return;
            }if(Integer.parseInt(sStart) >= Integer.parseInt(sEnd)){
                JOptionPane.showMessageDialog(theParent, "I'm sorry, the start value must be lower than the end value.");
                return;
            }
        } else if ((tempV.getHasSaved() && !tempV.isNumber()) || (!tempV.getHasSaved() && tabPane.getSelectedIndex() == 0)) {
            if (tempV.getInstances().size() <= 1) {
                JOptionPane.showMessageDialog(theTab, "I'm sorry, there needs to be more than one instance of this word variable.");
                return;
            }
        } else {
            JOptionPane.showMessageDialog(theTab, "Error Variable Controller - this else statement shouldn't be accessible.");
            return;
        }

        System.out.println("check 2");

        //1) error checking var name/iterate
        String tempN = fVarName.getText();
        String tempIs = fIterate.getText();

        if(!eVarName.hasNoErrors(tempN) || !eIterate.hasNoErrors(tempIs)){
            return;
        }

        System.out.println("check 3");

        //no errors? - make a new variable
        int tempI = Integer.parseInt(tempIs);
        tempV.setName(tempN);
        tempV.setIterate(tempI);
        if(isAnumber){
            int minValue = Integer.parseInt(fStart.getText());
            int maxValue = Integer.parseInt(fEnd.getText());
            tempV.setMaxValue(maxValue);
            tempV.setMinValue(minValue);
            listModel.clear();
            listModel.addElement("" + minValue + " - " + maxValue);
        }
        //3) set saved
        if(!tempV.getHasSaved()){
            tempV.setSaved(true);
            tempV.setIsNumber(isAnumber);
            currentTrigger.removeVariable(tempV);
            currentTrigger.addVariable(tempV);
        }else{//if already saved, overwrite the current variable
            String oldVarName = ((Variable)cbVars.getSelectedItem()).getName();


            System.out.println("it has to");
            currentTrigger.changeVariable(oldVarName, tempV);

            System.out.println("be this");
        }

        System.out.println("check 4 dondndndn");
        
        cbVars.repaint();
        selectVariable();
    }

    private void deleteVariable() {
        boolean variablesExist = (cbVars.getItemCount() != 0);
        if (!variablesExist) {
            JOptionPane.showMessageDialog(theTab, "I'm sorry, there are no variables for you to delete.");
            return;
        }

        int index = cbVars.getSelectedIndex();
        Variable tempV = (Variable)cbVars.getItemAt(index);
        cbVars.removeItemAt(index);
        if(cbVars.getItemCount() != 0){
            if(index != 0){
                cbVars.setSelectedIndex(index - 1);
            }
        }
        if(tempV.getHasSaved()){
            currentTrigger.removeVariable(tempV);
        }
        if(cbVars.getItemCount() == 0){
            noVariables();
        }

    }

    private void setFieldsToDefault() {
        fInstance.setText("<instance>");
        fStart.setText("<start>");
        fEnd.setText("<end>");
        fIterate.setText("<number>");
        tabPane.setSelectedIndex(0);
        tabPane.setEnabledAt(0, true);
        tabPane.setEnabledAt(1, true);
    }

    private void noVariables() {
        setFieldsToDefault();
        cbOptions.setSelectedIndex(0);
        fVarName.setText("<var name>");
        fEnd.setEnabled(false);
        fIterate.setEnabled(false);
        fStart.setEnabled(false);
        fInstance.setEnabled(false);
        fVarName.setEnabled(false);

        cbVars.setEnabled(false);
        bSubInstance.setEnabled(false);
        bChangeInstance.setEnabled(false);
        tabPane.setEnabled(false);
        cbOptions.setEnabled(true);
        bSubOptions.setEnabled(true);
        listInstance.setModel(new DefaultListModel());
        listModel = (DefaultListModel)listInstance.getModel();
        listInstance.setEnabled(false);
        noVars = true;
    }

    private void variableNowExists() {
        fEnd.setEnabled(true);
        fIterate.setEnabled(true);
        fStart.setEnabled(true);
        fInstance.setEnabled(true);
        fVarName.setEnabled(true);
        cbVars.setEnabled(true);
        bSubInstance.setEnabled(true);
        bChangeInstance.setEnabled(true);
        tabPane.setEnabled(true);
        listInstance.setEnabled(true);
        noVars = false;
    }
}
